﻿using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using Common.Library.HTTP;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Spider;
using Spider.Analyse;
using Spider.Init;
using Spider.Rules;
using Spider.Init.英语学习资料;
using Spider.Init.小说.八零电子书;
using Spider.Entitys.Spider;
using SpiderCore.Downloader;

namespace SpiderTest
{
    [TestClass]
    public class UnitTest1
    {
        #region 第一版爬虫
        
        #region 抓取省市县信息
        /// <summary>
        /// 第一个版本的爬虫
        /// 问题是爬虫的入口直接写到了 SpiderStart 类中
        /// 导致如果我想在写一个爬虫 抓取其他网站的页面，需要修改这个爬虫的代码
        /// </summary>
        [TestMethod]
        public void 抓取省市县信息()
        {
            SpiderStart ss = new SpiderStart(new 省市县HTML规则(), new 省市县规则解析());
            ss.Start("http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016");

            var res = SpiderClientHepler.Get("http://www.tianqihoubao.com/lishi/");

        }
        #endregion

        #region 天气预报抓取
        /// <summary>
        /// 第二版爬虫
        /// 实例化 SpiderStart 爬虫启动类的时候
        /// 需要指定一个爬虫模板
        /// 调用爬虫启动类的 Start() 方法是会直接启动爬虫模板
        /// </summary>
        [TestMethod]
        public void 天气预报抓取()
        {
            SpiderStart ss = new SpiderStart();
            ss.init = new Init天气预报();
            ss.Start();
        }
        #endregion

        #region 新概念英语第一套
        /// <summary>
        /// 第三版爬虫
        /// </summary>
        [TestMethod]
        public void 新概念英语第一套()
        {
            SpiderStart ss = new SpiderStart();
            ss.init = new Init新概念英语第一套();
            ss.Start();
        }
        #endregion

        #region 小说数据抓取

        #region 业务抓取逻辑
        //目标扒下书网里面的小说给抓取过来，按照分类，时间字数 等信息分门别类
        //为我的推荐系统准备基础数据


        ///   抓取目标八零电子书
        /// 先抓分类数据
        /// 先把分类数据写入到系统中
        /// 在抓取页面信息  页面信息直接保存到数据库中
        ///   再写一个程序用来清洗数据，把有用的数据保存到数据库中
        ///   

        /// 业务分析
        /// 一级页面
        ///     首页
        /// 二级页面
        ///     分类列表页面
        ///     这个是主要的抓取页面
        ///     通过这个页面抓取小时列表
        /// 三级页面
        ///     小说详情页面
        ///     抓取该小说的 详情，下载次数，评分，点击数量，状态等信息
        /// 四级页面
        ///     中转页面
        ///     小说的目录页面
        /// 五级页面
        ///     小说的章节页面 
        ///     
        /// 完成进度
        /// 分类信息已经录入完毕 (手动录入)
        /// 
        /// 列表页面
        /// 手动获取所有的列表页面
        /// 通过爬虫抓取所有列表页面的URL获取到对应的详情页面
        ///     这里我想使用多线程的方式去弄
        #endregion

        #region 代码实现
        [TestMethod]
        public void 八零单子书抓取()
        {
            SpiderStart ss = new SpiderStart();
            ss.init = new Init八零列表();
            ss.Start();
        }
        #endregion

        #endregion

        #endregion

        #region 第二版爬虫

        #region 阶段一
        /// 第一版爬虫遇到的问题
        ///     直接的问题是，抓取八零单子书的时候 模拟web请求，响应超时
        ///     欠缺的是对于 httpclient 的使用经验
        ///     缺少HTTP协议的相关理解
        /// 第二版
        ///     开始直接到GitHub上面获取别人的爬虫项目，把别人多个爬虫项目集合到我的项目里面
        /// 我的爬虫
        ///     抓取模块
        ///         如何伪装成一个正常的浏览器请求
        ///         Cookie
        ///             如果伪造一份Cookie数据
        ///             当多次请求的时候Cookie如何伪造
        ///         下载文件
        ///             我之前下载新概念英语第一册的音频资料的时候部分MP3文件下载失败，这个问题如何解决
        ///         HTTP压缩
        ///         HTTP缓存
        ///         汉语中不同文字编码的网站如何识别
        ///         
        ///     爬虫模块
        ///         爬虫模块主要是负责，解析抓取后的HTML页面，解析出来后面需要抓取的URL，并区分出来当前URL对应的资源格式
        ///         难点
        ///             有些URL是写在JS当中的，通过一个点击事件来跳转连接
        ///             如果区分已经抓取的URL
        ///             URL深度问题
        ///     持久化模块
        ///         通过策略模式，手动确定，数据持久化的方案，SQL server MongoDB Redis
        ///         如何实现热插拔
        /// 
        /// 
        /// 我的这个爬虫要是是一个通用爬虫，我输入一个地址时候他能直接的去抓取后面的页面信息
        /// 然后我再通过别的项目去解析出来我想要的数据
        /// 
        /// 参考SpiderCore 项目
        ///     
        /// </summary>
        /// 
        /// SpiderCore 项目架构
        /// Engines  引擎  项目从这里启动
        /// Scheduler   调度器
        /// Downloader  下载器
        /// Spiders     爬虫
        /// Pipeline    管道
        /// 
        /// 爬虫运行过程
        /// 引擎打开一个网站(open a domain)，找到处理该网站的Spider并向该spider请求第一个要爬取的URL。
        /// 引擎从Spider中获取到第一个要爬取的URL并在调度器(Scheduler)以Request调度。
        /// 引擎向调度器请求下一个要爬取的URL。
        /// 调度器返回下一个要爬取的URL给引擎，引擎将URL通过下载中间件(请求(request) 方向)转发给下载器(Downloader)。
        /// 一旦页面下载完毕，下载器生成一个该页面的Response，并将其通过下载中间件(返回(response) 方向)发送给引擎。
        /// 引擎从下载器中接收到Response并通过Spider中间件(输入方向)发送给Spider处理。
        /// Spider处理Response并返回爬取到的Item及(跟进的)新的Request给引擎。
        /// 引擎将(Spider返回的)爬取到的Item给Item Pipeline，将(Spider返回的)Request给调度器。
        /// (从第二步) 重复直到调度器中没有更多地request，引擎关闭该网站。
        /// 
        /// Engines 初始化 下载器，调度器，爬虫，管道
        /// Scheduler 调度器，从调度器中，弹出一个 RequestModel
        /// Downloader 下载器 把 RequestModel 传入到下载器中，下载 RequestModel 中的信息
        /// Pipeline 管道 把下载器 Down 下来的 ResponseModel 给管道，管道解析 HTML，获取到所有的连接，创建 List<RequestModel> 插入到调度器中
        /// 
        /// </summary>
        #endregion

        #region 阶段一问题解决
        /// 网站压缩 问题已经解决
        /// 伪造请求头信息 在输入爬虫种子的时候就已经把相关的 请求头信息写入到了请求对象中，爬虫在提取页面信息的时候直接把请求对象进行浅拷贝
        /// 
        /// 
        /// </summary>
        #endregion

        #region 阶段二  有些东西需要确定一下
        /// 有些东西需要确定一下
        /// 问题一
        ///     多线程的情况下，下载器是否会冲突
        /// 问题二
        ///     Ajax页面加载完毕才会加载列表数据，这种问题如何解决
        /// 问题三
        ///     URL信息在Ajax中通过点击事件传递当前元素的参数，这种问题如何解决
        /// 
        /// </summary>
        #endregion

        #region 抓取中遇到的问题 下书网
        /// 详情页
        ///     详情页中有 小说列表的信息，但是需要点击加载更多按钮来加载出来所有的列表信息
        ///     评论信息，也是这个套路
        /// 解决方案：
        ///     通过URL区分页面类型
        ///     通过页面类型使用不同的  爬虫解析
        /// 
        #endregion

        #endregion

        #region 可配置化思路/需求
        /// 需求：等框架完成后，我希望每次想下载新的页面的时候可以不对现有的框架进行任何的修改
        ///       直接把框架当成一个组件使用
        /// 
        #region 下载器的可配置
        /// 下载器的可配置
        /// 同一个网站不同的页面使用不同的下载器，实现可配置
        ///      创建一个下载器工厂
        ///      把下载器工厂给爬虫引擎
        ///      引擎通过工厂获取对应的  下载器 
        #endregion
        /// 
        /// 
        #endregion

        #region 爬虫框架  第一阶段目标
        /// 把爬虫模块，彻底封装成一个组件出来，可以插入到任何一个 .NET 项目中使用
        /// 模块 == 组件
        /// 他们侧重点不同
        /// 模块 -> 职责分离
        /// 组件 -> 单元复用
        /// 
        /// 
        #endregion

        #region HttpClient 请求测试
        [TestMethod]
        public void 测试Get请求()
        {
            string url = "http://10.7.19.39:8900/api/v1/customer/shenzhen/district-area";
            var res = HttpClientBearerToken.Get(url);
        }

        [TestMethod]
        public void 测试Post请求()
        {


            StringContent content = new StringContent("{\"callState\":0,\"system\":0,\"empNo\":\"string\",\"cityId\":\"string\",\"priceFrom\":0,\"priceTo\":0,\"areaFrom\":0,\"areaTo\":0,\"houseRegion\":\"string\",\"concern\":\"string\",\"operationTime\":\"2018-09-26T07:21:06.594Z\",\"mobile\":\"string\",\"customerName\":\"string\",\"sex\":0,\"buyReason\":0,\"createTime\":\"2018-09-26T07:21:06.595Z\"}");
            //以JSON的方法发送请求
            content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            var res = HttpClientBearerToken.Post("http://10.7.19.39:8900/api/v1/customer/backwrite-log", content);
            var json = res.Content.ReadAsStringAsync().Result;
        }
        #endregion

        #region 爬虫必备技能
        /// 正则表达式
        /// Request
        /// 常用的  验证和授权方式
        /// 
        #endregion


        [TestMethod]
        public void 下书网请求伪造分类页面()
        {
            string url = "https://www.xiashu.la/type/nan_0_0_allvisit_1.html";
            string encoding = "";
            var request = new RequestModel
            {
                Encoding = encoding,
                Url = url,
                Timeout = 10000,
                UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36",
                Sleep = 2000,
                Depth = 1,
                Source = "下书网",
                Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
                //Connection = "keep-alive",
                Header = new List<KeyValue>()
                {
                    new KeyValue("Accept-Encoding","gzip"),
                    new KeyValue("Accept-Language","zh-CN,zh;q=0.9"),
                    new KeyValue("Cache-Control","max-age=0"),
                },
                Cookie = new List<KeyValue>()
                {

                }
            };
            HttpDownloader hd = new HttpDownloader();
            var html = hd.Download(request);

        }

    }
}
